/**
 * Created by ari on 1/14/2015.
 */
var http = require("http"),
    fs	 = require("fs"),
    readline = require('readline'),
    url = require('url'),
    qs = require('querystring');

var activeRequests = [];
var pathOffsets = {};

var CLIENT_PORT = 7845;
var EVENT_PORT = 7844;

var TIMEOUT_MAX = 5000;

var COMMAND_ADD = 'add';
var COMMAND_PATH = 'path';
var COMMAND_ERROR = 'error';

http.createServer(function(request, response) {
    console.log("New Client connection: " + request.url);
    var url_parts = url.parse(request.url, true);
    var query = url_parts.query;
    if(!query || !query.path) {
        console.error("No paths provided " + request.url);
        response.writeHead(400, "No Paths, use ?path=[path],[offset];[path2],[offset2]...", {
            'Content-Type'   : 'text/plain',
            'Access-Control-Allow-Origin' : '*'
        });
        response.end();
        return;
    }

    response.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
    response.setHeader('Access-Control-Allow-Origin', '*');
    response.setHeader('ConnectionOrigin', 'keep-alive');
    response.setHeader('Cache-Control', 'no-cache');
    response.write("\nevent: event\ndata: welcome\n\n");
    //response.writeHead(206, "NodeJS Log Listener", {
    //    'Connection': 'Transfer-Encoding',
    //    'Content-Type'   : 'text/plain; charset=utf-8',
    //    'Transfer-Encoding': 'chunked',
    //    'Access-Control-Allow-Origin' : '*'
    //});
    request.on('close', function() {
        console.log("Removing " + request.url);
        for(var ii=0; ii<activeRequests.length; ii++) {
            if(activeRequests[ii][0] === request) {
                activeRequests.splice(ii, 1);
                return;
            }
        }
    });

    var paths = query.path || '';
    if(typeof paths === 'string')
        paths = paths.split(';');

    for(var i=0; i<paths.length; i++) {
        if(typeof paths[i] === 'string')
            paths[i] = paths[i].split(',');
        var path = paths[i][0];
        var offset = parseInt(paths[i][1]);
        pathOffsets[path] = offset;
        paths[i] = path;

        watchFile(request, response, path, offset);
    }
    //activeRequests[activeRequests.length] = [request, response, paths];

}).listen(CLIENT_PORT);
console.log('Server running at :' + CLIENT_PORT + '/');


http.createServer(function(request, response) {
    var path = request.url;
    if(!path.match(/\.log$/))
        throw new Error("Invalid log path");
    var url_parts = url.parse(request.url, true);
    var query = url_parts.query;
    var content = request.body;
    var matchedRequests = [];
    var filePath = request.url;
    for(var i=0; i<activeRequests.length; i++) {
        var info = activeRequests[i];
        var paths = info[2];
        if(paths.indexOf(filePath) >= 0)
            matchedRequests[matchedRequests.length] = info;
    }
    request.on('data', function (data) {
        for(var i=0; i<matchedRequests.length; i++) {
            var info = matchedRequests[i];
            var req = info[0];
            var res = info[1];
            var paths = info[2];
            var offset = pathOffsets[filePath];
            if(!offset)
                throw new Error("Path missing offset");

            console.log("Writing to " + req.url);
            response.write("id: " + COMMAND_PATH + "\n");
            response.write("data: " + filePath + ":" + offset + "\n");
            res.write(data);
            pathOffsets[filePath] = offset + data.length;
            console.log("Updated offset: " + pathOffsets[filePath] + ' - ' + filePath);
        }
        //
        //// Too much POST data, kill the connection!
        //if (body.length > 1e6)
        //    request.connection.destroy();
    });

}).listen(EVENT_PORT);
console.log('Server running at :' + EVENT_PORT + '/');


function writeLine(response, args) {
    response.write("event: line\ndata: " + args.join(' ') + "\n\n"); // TODO: proper eventsource
    console.log("event: line\ndata: " + args.join(' '));
}

function watchFile(request, response, filePath, offset) {
    console.log("Checking path: " + filePath + ":" + offset);
    var stats = fs.statSync(filePath);

    if (stats.size < offset) {
        writeLine(response, [COMMAND_ERROR, "Size (" + stats.size + ") < Offset (" + offset + ")"]);
        console.error("Size (" + stats.size + ") < Offset (" + offset + ")");
        return false;
    }

    // if the file is changed
    if (stats.size > offset) {
        console.log("Size (" + stats.size + ") > Offset (" + offset + ")");

        // read it
        var fd = fs.openSync(filePath, 'r');
        var instream = fs.createReadStream(filePath, {
            encoding: 'utf-8',
            bufferSize: 1024,
            start: offset,
            end: stats.size,
            fd: fd
        });

        var rl = readline.createInterface({
            input: instream,
            terminal: false
        });
        response.write("event: path\ndata: " + filePath + ":" + stats.size + "\n\n");
        console.log("event: path\ndata: " + filePath + ":" + stats.size + "\n\n");

        rl.on('line', function(line) {
            writeLine(response, [line]);
        });

        rl.on('close', function(line) {
            response.write("event: eof\ndata: " + filePath + ":" + stats.size + "\n\n");
            console.log("event: eof\ndata: " + filePath + ":" + stats.size + "\n\n");
        });

        offset = stats.size;
        pathOffsets[filePath] = offset;
        console.log("Updated offset: " + pathOffsets[filePath] + ' - ' + filePath);
    }

    fs.watchFile(filePath, {
            persistent: true,
            interval: 100
        },
        function(curr, prev) {
            if (curr.size <= prev.size) {
                console.error("size: %d <= %d", prev.size, curr.size);
                return;
            }
            console.log("size: %d > %d", prev.size, curr.size);

            var fd = fs.openSync(filePath, 'r');

            var instream = fs.createReadStream(filePath, {
                encoding: 'utf-8',
                bufferSize: 1024,
                start: prev.size,
                end: curr.size,
                fd: fd
            });

            var rl = readline.createInterface({
                input: instream,
                terminal: false
            });
            response.write("event: path\ndata: " + filePath + ":" + stats.size + "\n\n");
            console.log("event: path\ndata: " + filePath + ":" + stats.size + "\n\n");

            rl.on('line', function(line) {
                writeLine(response, [line]);
            });
            rl.on('error', function(line) {
                writeLine(response, [COMMAND_ERROR, line]);
            });

            rl.on('close', function(line) {
                response.write("event: eof\ndata: " + filePath + ":" + stats.size + "\n\n");
                console.log("event: eof\ndata: " + filePath + ":" + stats.size + "\n\n");
            });

        }
    );

    response.on('close', function() {
        console.log("Unwatching: " + filePath);
        fs.unwatchFile(filePath);
    });

    console.log("Watching path: " + filePath + ":" + offset);
}
